﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Configuration;
using System.Linq;
using System.Text;
using Fintech.Utils;
using Fintech.Utils.Diagnostics;
using ZOVNP.Model.Abstracts;

namespace ZOVNP.Engine
{
    /// <summary> Настройки для режима отладки. </summary>
    [UserDescriptionSettings("Настройки отладки")]
    internal class DebugSettings : ApplicationSettingsBase
    {
        #region Поля

        private bool initializing;
        private static DebugLoggerType defaultLogger = DebugLoggerType.File;
        private static DebuggerSupportedLevel defaultLevel = DebuggerSupportedLevel.Error;
        private static bool defaultAssertions = true;
        private static bool defaultTime = true;
        private static string defaultFile = Environment.GetFolderPath(System.Environment.SpecialFolder.Desktop) + "\\Debug.log";

        #endregion

        #region Конструкторы

        /// <summary> Создать экземпляр настроек отладки. </summary>
        public DebugSettings()
        {
            try // Обращение в блоке try нужно для первой загрузки настроек, когда их конфигурационного файла ещё нет.
            {
                var value = AllowAssertions;// this["FileName"].CheckOnCast<string>("FileName"); // Эта строка инициализирует настройки, если их ещё нет.
            }
            catch (Exception)
            {
                SetDefaults();
            }
        }

        #endregion

        #region Свойства

        /// <summary> Тип интерфейса для вывода отладочных сообщений. </summary>
        [DisplayName("Интерфейс вывода")]
        [Description("Тип интерфейса для вывода отладочных сообщений.")]
        [Category("Отладка")]
        [DefaultValue(DebugLoggerType.File)]
        [UserScopedSetting]
        [TypeConverter(typeof(EnumDescriptionConverter))]
        public DebugLoggerType Logger
        {
            get
            {
                var value = this["Logger"];
                return value != null ? (DebugLoggerType)value : defaultLogger;
            }
            set
            {
                this["Logger"] = value;
                if (initializing) return;
                var old = DebugTuner.Instance.Logger;
                DebugTuner.Instance.Logger = new NullLogger();
                old.Dispose();
                DebugTuner.Instance.Logger = GetLogger();
            }
        }

        /// <summary> Уровень срабатывания программных точек останова. </summary>
        [DisplayName("Точки останова")]
        [Description("Уровень срабатывания программных точек останова.")]
        [Category("Отладка")]
        [DefaultValue(DebuggerSupportedLevel.Error)]
        [TypeConverter(typeof(EnumDescriptionConverter))]
        [UserScopedSetting]
        public DebuggerSupportedLevel DebuggerSupportedLevel
        {
            get
            {
                var value = this["DebuggerSupportedLevel"];
                return value != null ? (DebuggerSupportedLevel)value : defaultLevel;
            }
            set
            {
                this["DebuggerSupportedLevel"] = value;
                if (initializing) return;
                DebugTuner.Instance.DebuggerSupportedLevel = value;
            }
        }

        /// <summary> Разрешать ли вычиление контрольных выражений (Asserts) в коде программы. </summary>
        [DisplayName("Вычисление контрольных выражений")]
        [Description("Разрешать ли вычиление контрольных выражений (Asserts) в коде программы.")]
        [Category("Отладка")]
        [DefaultValue(true)]
        [UserScopedSetting]
        [TypeConverter(typeof(RUBooleanConverter))]
        public bool AllowAssertions
        {
            get
            {
                var value = this["AllowAssertions"];
                return value != null ? (bool)value : defaultAssertions;
            }
            set
            {
                this["AllowAssertions"] = value;
                if (initializing) return;
                DebugTuner.Instance.AssertionsEnabled = value;
            }
        }

        /// <summary> Разрешать запись в интерфейс вывода временной метки сообщения. </summary>
        [DisplayName("Временная метка")]
        [Description("Разрешать запись в интерфейс вывода временной метки сообщения.")]
        [Category("Отладка")]
        [DefaultValue(true)]
        [UserScopedSetting]
        [TypeConverter(typeof(RUBooleanConverter))]
        public bool ShowTime
        {
            get
            {
                var value = this["ShowTime"];
                return value != null ? (bool)value : defaultTime;
            }
            set
            {
                this["ShowTime"] = value;
                if (initializing) return;
                DebugTuner.Instance.Logger.ShowTime = value;
            }
        }

        /// <summary> Файл для записи сообщений (для интерфейса записи в файл). </summary>
        [DisplayName("Файл журнала")]
        [Description("Файл для записи сообщений (для интерфейса записи в файл).")]
        [Category("Отладка")]
        [DefaultValue("Debug.log")]
        [UserScopedSetting]
        [Editor("Fintech.Controls.FileNameEditor, Controls", typeof(System.Drawing.Design.UITypeEditor))]
        public string FileName
        {
            get
            {
                var value = this["FileName"];
                return value != null ? (string)value : defaultFile;
            }
            set
            {
                this["FileName"] = value;
                if (initializing) return;
                var logger = DebugTuner.Instance.Logger;
                var casted = logger as FileLogger;
                if (casted != null)
                    casted.ChangeFile(value);
            }
        }

        #endregion

        #region Методы

        /// <summary> Получение интерфейса вывода отладочной печати. </summary>
        /// <param name="Settings"> Используемые настройки. </param>
        /// <returns> Интерфейс печати. </returns>
        private DebugLogger GetLogger()
        {
            switch (Logger)
            {
                case DebugLoggerType.Console: return new ConsoleLogger(ShowTime);
                case DebugLoggerType.File: return new FileLogger(FileName, ShowTime);
                case DebugLoggerType.None: return new NullLogger();
                case DebugLoggerType.Output: return new OutputWriter(ShowTime);
                default: throw new NotSupportedException();
            }
        }

        /// <summary> Установить значения по умолчанию. </summary>
        private void SetDefaults()
        {
            initializing = true;
            try
            {
                Logger = defaultLogger;
                DebuggerSupportedLevel = defaultLevel;
                AllowAssertions = defaultAssertions;
                ShowTime = defaultTime;
                FileName = defaultFile;
            }
            finally
            {
                initializing = false;
            }
        }

        //TODO: [Максимов Иван] Save() должен быть по Clone(), который напишет Олег]
        protected override void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            base.OnPropertyChanged(sender, e);
            Save();
        }

        /// <summary> Применить настройки. </summary>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:DisposeObjectsBeforeLosingScope")]
        public void Apply()
        {
            DebugTuner.Instance = new DebugTuner(DebuggerSupportedLevel, AllowAssertions, GetLogger());
        }

        #endregion
    }
}
